import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
import 'flutter_qt_7ze_bindings_generated.dart';

const String _libName = 'flutter_qt_7ze';

class NoAndroidWarper {
  NoAndroidWarper._privateConstructor() {
    //todo 私有构造方法
  }

  static final NoAndroidWarper _instance =
      NoAndroidWarper._privateConstructor();

  factory NoAndroidWarper() {
    return _instance;
  }

  final FlutterQt7zeBindings _bindings = FlutterQt7zeBindings(() {
    if (Platform.isMacOS || Platform.isIOS) {
      return DynamicLibrary.open('$_libName.framework/$_libName');
    }
    if (Platform.isAndroid || Platform.isLinux) {
      return DynamicLibrary.open('lib$_libName.so');
    }
    if (Platform.isWindows) {
      return DynamicLibrary.open('$_libName.dll');
    }
    throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
  }());

  Map requestMap = {};
  int _nextSumRequestId = 0;

  /// The SendPort belonging to the helper isolate.
  final Future<SendPort> _helperIsolateSendPort = () async {
    // The helper isolate is going to send us back a SendPort, which we want to
    // wait for.
    final Completer<SendPort> completer = Completer<SendPort>();

    // Receive port on the main isolate to receive messages from the helper.
    // We receive two types of messages:
    // 1. A port to send messages on.
    // 2. Responses to requests we sent.
    final ReceivePort receivePort = ReceivePort()
      ..listen((dynamic data) {
        if (data is SendPort) {
          // The helper isolate sent us the port on which we can sent it requests.
          completer.complete(data);
          return;
        }
        if (data is Map) {
          if (data['extractFile']) {
            var requestId = data['requestId'];
            final Completer<bool> completer =
                NoAndroidWarper().requestMap[requestId];
            NoAndroidWarper().requestMap.remove(requestId);
            completer.complete(data['ret'] == 0);
          }
          return;
        }
        throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
      });

    // Start the helper isolate.
    await Isolate.spawn((SendPort sendPort) async {
      final ReceivePort helperReceivePort = ReceivePort()
        ..listen((dynamic data) {
          // On the helper isolate listen to requests and respond to them.
          if (data is Map) {
            if (data['func'] == 'extractFile') {
              final int result = NoAndroidWarper()
                  ._bindings
                  .extractFile(data['fileName'], data['desPath']);
              sendPort.send({
                'ret': result,
                'requestId': data['requestId'],
                'func': 'extractFile'
              });
            }
            return;
          }
          throw UnsupportedError(
              'Unsupported message type: ${data.runtimeType}');
        });

      // Send the the port to the main isolate on which we can receive requests.
      sendPort.send(helperReceivePort.sendPort);
    }, receivePort.sendPort);

    // Wait until the helper isolate has sent us back the SendPort on which we
    // can start sending requests.
    return completer.future;
  }();

  Future<bool> extractFile(
      {required String fileName, required String desPath}) async {
    final SendPort helperIsolateSendPort = await _helperIsolateSendPort;

    final int requestId = _nextSumRequestId++;
    final Completer<bool> completer = Completer<bool>();
    requestMap[requestId] = completer;

    helperIsolateSendPort.send({
      "func": 'extractFile',
      'requestId': requestId,
      'fileName': fileName,
      'desPath': desPath
    });

    return completer.future;
  }

  String getLzmaVersion() {
    return (_bindings.getLzmaVersion() as Pointer<Utf8>).toDartString();
  }
}

class AndroidWarper {
  AndroidWarper._privateConstructor() {
    //todo 私有构造方法
  }

  static final AndroidWarper _instance = AndroidWarper._privateConstructor();

  factory AndroidWarper() {
    return _instance;
  }
  MethodChannel methodChannel = const MethodChannel('qt_7ze_flutter');

  Future<bool?> extractFile(
      {required String fileName,
      required String desPath,
      required bool isAndroidAssets}) {
    if (isAndroidAssets) {
      return methodChannel.invokeMethod<bool>('extractAsset',
          jsonEncode({'fileName': fileName, 'desPath': desPath}));
    } else {
      return methodChannel.invokeMethod<bool>('extractFile',
          jsonEncode({'fileName': fileName, 'desPath': desPath}));
    }
  }

  Future<String?> getLzmaVersion() {
    return methodChannel.invokeMethod<String>('getLzmaVersion');
  }
}

Future<bool?> extractFile(
    {required String fileName,
    required String desPath,
    required bool isAndroidAssets}) {
  if (Platform.isAndroid) {
    return AndroidWarper().extractFile(
        fileName: fileName, desPath: desPath, isAndroidAssets: isAndroidAssets);
  } else {
    return NoAndroidWarper().extractFile(fileName: fileName, desPath: desPath);
  }
}

Future<String?> getLzmaVersion() {
  if (Platform.isAndroid) {
    return AndroidWarper().getLzmaVersion();
  } else {
    return Future(() {
      return NoAndroidWarper().getLzmaVersion();
    });
  }
}
